
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netdb.h>

#include <librally.h>

Err rp_comm_connect(int fd, const struct sockaddr_in * addr, socklen_t length)
{
	static char fname[] = "rp_comm_connect";
	static char msg[] = "fatal error";
	Err err;	

	int i = connect(fd, (struct sockaddr *)addr, length) ;

	if (i < 0)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot connect to %s:%d. Error: %s", strerror(errno));	// ip:port
		close(fd);
		return err;
	}
	
	return err;
}

int rp_comm_accept(int fd, struct sockaddr_in *address, socklen_t *address_len)
{
	static char fname[] = "rp_comm_accept";
	static char msg[] = "fatal error";
	Err err;
	int client;

	if ((client = accept(fd, (struct sockaddr *)address, address_len)) < 0)
	{
		rp_log(LOG_CRITICAL, fname, "Failed to accept incoming connection request at %s:%d. Error: %s", strerror(errno)); // ip:port
	}
	return client;
}

int rp_comm_send(int fd, const void *buf, int size)
{
	static char fname[] = "rp_comm_send";
	static char msg[] = "fatal error";
	Err err;
	ssize_t count;

	count = send(fd, buf, (size_t)size, 0);
	if ( count < 0)
	{
		rp_log(LOG_CRITICAL, fname, "Error happens during the send operation. Error: %s", strerror(errno)); // ip:port		
	} else if (count == 0) 
	{
		rp_log(LOG_DEBUG, fname, "The connection has been shutdown by the remote endpoint", strerror(errno)); // ip:port		
	}


	return count;
}

int rp_comm_recv(int fd, void *buf, int size)
{
	static char fname[] = "rp_comm_send";
	static char msg[] = "fatal error";
	Err err;
	ssize_t count;

	count = recv(fd, buf, (size_t)size, 0);
	if ( count  < 0)
	{
		rp_log(LOG_CRITICAL, fname, "Error happens during the receive operation. Error: %s", strerror(errno)); // ip:port		
	} else if (count == 0) 
	{
		rp_log(LOG_DEBUG, fname, "The connection has been shutdown by the remote endpoint", strerror(errno)); // ip:port		
	}

	return count;
}

int rp_comm_socket(int type, int mode)
{
	static char fname[] = "rp_comm_socket";
	static char msg[] = "fatal error";
	Err err;
	int fd;
	int flag;
	
    	if ((fd = socket(AF_INET, type, 0)) < 0) 
	{
		rp_log(LOG_CRITICAL, fname, "Failed to create sockets. Error: %s", strerror(errno));
		return fd;
    	}	
	/*	set the socket in non-blocking mode   */
	if (mode == COMM_NONBLOCK)
	{
		if ((flag = fcntl(fd, F_GETFL, 0)) < 0)
		{
			rp_log(LOG_CRITICAL, fname, "Failed to get socket flag. Error: %s", strerror(errno));
			close(fd);
			return -1;
		}
		if (fcntl(fd, F_SETFL, flag|O_NONBLOCK) < 0)
		{
			rp_log(LOG_CRITICAL, fname, "Failed to set socket to Non-block mode. Error: %s", strerror(errno));
			close(fd);
			return -1;		
		}
	}
	return fd;
}

Err rp_comm_close(int fd)
{
	static char fname[] = "rp_comm_close";
	static char msg[] = "fatal error";
	Err err;

	close(fd);

	return err;
}

Err rp_comm_tcp_server_init(int *fd, struct sockaddr_in *addr, int mode)
{
	static char fname[] = "rp_comm_server_tcp_server_init";
	static char msg[] = "fatal error";
	Err err;
	
    	/* init socket. */    
	*fd = rp_comm_socket(SOCK_STREAM, mode);		// TCP
	if (*fd < 0) 
	{        
		rp_log(LOG_CRITICAL, fname, "Failed to initialize TCP server. Error: %s", strerror(errno));
		return err; 
	}    
/*
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
    if (is_udp) {
        maximize_sndbuf(sfd);
    } else {
        setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
        setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
        setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
    }
*/
	/* bind() */    
	if (bind(*fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) 
	{        
		rp_log(LOG_CRITICAL, fname, "Failed to call bind(). Error: %s", strerror(errno));
		close(*fd);
		return err; 
	}    

	/* listen() */    
	if (listen(*fd, 1024) < 0) 
	{       
		close(*fd);
		rp_log(LOG_CRITICAL, fname, "Failed to call listen(). Error: %s", strerror(errno));
		return err; 
	}    	
	
	return err;
}

Err rp_comm_tcp_server_finalize(int fd)
{
	static char fname[] = "rp_comm_tcp_server_finalize";
	static char msg[] = "fatal error";
	Err err;
	
	err = rp_comm_close(fd);

	return err;
}

Err rp_comm_tcp_client_init(int *fd, struct sockaddr_in *addr, int mode)
{
	static char fname[] = "rp_comm_tcp_client_init";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);
	
	if (!addr)
	{
		rp_log(LOG_DEBUG, fname, "Missing IP address or Port number");
		return E_INVALID;
	}

	*fd =rp_comm_socket(SOCK_STREAM, mode);
	
	err = rp_comm_connect(*fd, addr, sizeof(*addr));	
	if (err)
	{
		return err;
	}
	
	FLOG_OUT(fname);
	return E_OK;
}
	


